package com.hnx.admin.aurora.utils;

import com.hnx.admin.aurora.annotation.Excel;
import com.hnx.admin.aurora.annotation.Excels;
import com.hnx.admin.aurora.web.exception.ListEmptyException;
import com.hnx.admin.aurora.web.exception.excel.ExcelExportException;
import com.hnx.admin.aurora.web.json.ExcelField;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/*
项目名称： admin-aurora
创 建 人: 倔强的头发
创建日期： 2025/6/23   7:18:17 
描   述：
*/
@Slf4j
public class ExcelUtil {
    private ExcelUtil() {
        // 私有构造器防止实例化
    }

    private final static ZoneId DEFAULT_ZONE = ZoneId.systemDefault();

    /**
     * 导出Excel（支持展开行）
     */
    public static <T> void exportExcel(HttpServletResponse response, List<T> list,
                                       String sheetName, Class<T> clazz) throws IOException, NoSuchFieldException {
        Objects.requireNonNull(response, "HttpServletResponse不能为空");
        Objects.requireNonNull(clazz, "目标类类型不能为空");
        if (list == null || list.isEmpty()) {
            throw new ListEmptyException();
        }
        // 创建工作簿
        Workbook workbook = new XSSFWorkbook();
        // 创建工作表
        Sheet sheet = workbook.createSheet(determineSheetName(sheetName));
        List<ExcelField> elist = getExcelFields(clazz, Excel.Type.EXPORT);
        createTitle(sheet, elist);
        createExcelData(workbook, sheet, list, elist);
        setResponseProperties(response, sheetName);
        try (OutputStream outputStream = response.getOutputStream()) {
            workbook.write(outputStream);
        } catch (IOException e) {
            log.error("Excel导出失败: {}", e.getMessage(), e);
            throw new ExcelExportException();
        } finally {
            closeWorkbook(workbook);
        }
    }

    /**
     * 设置响应属性
     */
    private static void setResponseProperties(HttpServletResponse response, String sheetName) {
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        String encodedFileName = encodeFileName(sheetName + ".xlsx");
        response.setHeader("Content-Disposition", "attachment;filename=" + encodedFileName);
    }

    /**
     * 编码文件名
     */
    private static String encodeFileName(String fileName) {
        return URLEncoder.encode(fileName, StandardCharsets.UTF_8)
                .replace("+", "%20");
    }

    /**
     * 确定工作表名称
     */
    private static String determineSheetName(String sheetName) {
        return StringUtils.isEmpty(sheetName) ? "Sheet1" : sheetName;
    }

    /**
     * 安全关闭工作簿
     */
    private static void closeWorkbook(Workbook workbook) {
        try {
            if (workbook != null) {
                workbook.close();
            }
        } catch (IOException e) {
            log.warn("关闭Workbook时发生错误: {}", e.getMessage());
        }
    }

    /**
     * 创建Excel数据行
     *
     * @param workbook    Excel工作簿对象
     * @param sheet       Excel工作表对象
     * @param dataList    数据列表
     * @param excelFields Excel字段配置列表
     * @param <T>         数据类型泛型
     * @throws ReflectiveOperationException 当反射操作失败时抛出
     */
    private static <T> void createExcelData(Workbook workbook,
                                            Sheet sheet,
                                            List<T> list,
                                            List<ExcelField> elist) throws NoSuchFieldException {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        AtomicInteger rowNum = new AtomicInteger(1);
        list.forEach(data -> {
            Row row = sheet.createRow(rowNum.getAndIncrement());
            IntStream.range(0, elist.size()).forEach(cellIndex -> {
                ExcelField excelField = elist.get(cellIndex);
                Excel excel = excelField.getExcel();
                Field field = excelField.getField();
                row.setHeightInPoints(excel.height());
                Cell cell = row.createCell(cellIndex);
                try {
                    Object value = getFieldValue(data, field);

                    setCellValue(cell, value, excel);
                } catch (Exception e) {
                    cell.setCellValue(excel.defaultValue());
                }
            });
        });
        /*
        CellStyle dataStyle = createDataStyle(workbook);
        AtomicInteger rowNum = new AtomicInteger(1);
        list.forEach(data -> {
            Row row = sheet.createRow(rowNum.getAndIncrement());
            row.setHeightInPoints(20);

            for (int i = 0; i < elist.size(); i++) {
                ExcelField column = elist.get(i);
                Cell cell = row.createCell(i);
                cell.setCellStyle(dataStyle);

                try {
                    Object value = getFieldValue(data, column);
                    setCellValue(cell, value, column.excel);
                } catch (Exception e) {
                    cell.setCellValue(column.excel.defaultValue());
                }
            }
        });*/
//        int rowNum = 1;
//        for (T item : list) {
//            Row row = sheet.createRow(rowNum++);
//            for (int i = 0; i < elist.size(); i++) {
//                ExcelField excelField = elist.get(i);
//                Excel excel = excelField.getExcel();
//                Field field = excelField.getField();
//                Object value = getValue(field, item, excel);
//                Cell cell = row.createCell(i);
//                row.setHeightInPoints(excel.height());
//                if (!StringUtils.isNotEmpty(excel.target()) || excelField.getField().getType().getName().contains("java.util.List")) {
//                    cell.setCellValue(value == null ? excel.defaultValue() : value.toString());
//                } else {
//                    Field declaredField = value.getClass().getDeclaredField(excel.target());
//                    Object o = getValue(declaredField, value, excel);
//                    cell.setCellValue(o == null ? excel.defaultValue() : o.toString());
//                }
//            }
//        }
    }

    /**
     * 获取字段值（增强支持嵌套属性和集合类型）
     */

    private static Object getFieldValue(Object obj, Field field) {
        field.setAccessible(true);
//        Excel excel = field.getAnnotation(Excel.class);

        // 处理嵌套属性
//        if (!StringUtils.isEmpty(excel.prefix())) {
//            return getNestedFieldValue(obj, field, excel.prefix());
//        }

        // 处理集合类型
//        if (excel.isCollection()) {
//            List<?> list = (List<?>) field.get(obj);
//            if (CollectionUtils.isEmpty(list)) {
//                return "";
//            }

        // 将集合转换为字符串，用分隔符连接
//            return list.stream()
//                    .map(item -> convertObjectToString(item, excel))
//                    .collect(Collectors.joining(excel.separator()));
//        }

        // 普通属性
        try {
            return field.get(obj);
        } catch (Exception e) {
            return null;
        }
    }

    private static Object getValue(Field declaredField, Object obj, Excel excel) {
        try {
            declaredField.setAccessible(true);
            Object value = declaredField.get(obj);
            if (Objects.isNull(value)) {
                return null;
            }
            if (declaredField.getType().getName().contains("java.util.List")) {

                ArrayList arr = new ArrayList();
                List<?> list = (List<?>) value;
                for (Object item : list) {
                    Class<?> aClass = item.getClass();
                    Field declaredField1 = aClass.getDeclaredField(excel.target());
                    arr.add(getValue(declaredField1, item, excel).toString());
                }
                return String.join(",", arr);
            }
            String exp = excel.exp();
            if (StringUtils.isNotEmpty(exp)) {
                value = convertByExp(value.toString(), exp, ",");
            }
            String format = excel.dateFormat();
            if (StringUtils.isNotEmpty(format)) {
                value = parseDateToStr(format, value);
            }
            return value;
        } catch (Exception ex) {
            log.error("导出excel时从对象反射获取值异常：" + ex.getMessage());
            return null;
        }
    }

    public static Object parseDateToStr(String dateFormat, Object val) {
        if (val instanceof Date) {
            return parseDate(dateFormat, (Date) val);
        } else if (val instanceof LocalDateTime) {
            return parseDate(dateFormat, toDate((LocalDateTime) val));
        } else if (val instanceof LocalDate) {
            return parseDate(dateFormat, toDate((LocalDate) val));
        }
        return val;
    }

    /**
     * 增加 LocalDateTime ==> Date
     */
    public static Date toDate(LocalDateTime temporalAccessor) {
        return Date.from(temporalAccessor.atZone(DEFAULT_ZONE).toInstant());
    }

    /**
     * 增加 LocalDate ==> Date
     */
    public static Date toDate(LocalDate temporalAccessor) {
        return Date.from(temporalAccessor.atStartOfDay(DEFAULT_ZONE).toInstant());
    }

    public static final String parseDate(final String format, final Date date) {
        return new SimpleDateFormat(format).format(date);
    }

    public static String convertByExp(String propertyValue, String converterExp, String separator) {
        if (StringUtils.isNotEmpty(propertyValue) && StringUtils.isNotEmpty(converterExp)) {
            String[] convertSource = converterExp.split(separator);
            for (String item : convertSource) {
                String[] values = item.split("=");
                if (Arrays.stream(values).anyMatch(n -> n.equals(propertyValue))) {
                    return values[1];
                }
            }
        }
        return null;
    }

    public static String convertByExp(String propertyValue, String converterExp) {
        return convertByExp(propertyValue, converterExp, ",");
    }

    /**
     * 创建数据行并设置行高
     */
    private static Row createDataRow(Sheet sheet, int rowIndex, List<ExcelField> excelFields) {
        Row row = sheet.createRow(rowIndex);
        // 使用第一个字段的高度配置作为行高
        excelFields.stream()
                .findFirst()
                .ifPresent(field -> row.setHeightInPoints(field.getExcel().height()));
        return row;
    }

    /**
     * 填充数据行内容
     */
//    private static <T> void fillDataRow(Row row, T item, List<ExcelField> excelFields) {
//        IntStream.range(0, excelFields.size()).forEach(cellIndex -> {
//            ExcelField excelField = excelFields.get(cellIndex);
//            Excel excelConfig = excelField.getExcel();
//            Field field = excelField.getField();
//            row.setHeightInPoints(excelConfig.height());
//            Cell cell = row.createCell(cellIndex);
//            try {
//                Object value = getFieldValue(field, item, excelConfig);
//                setCellValue(cell, value, excelConfig);
//                if(StringUtils.isNotEmpty(excelConfig.target())){
//                    Field declaredField = value.getClass().getDeclaredField(excelConfig.target());
//                    value = getFieldValue(declaredField, value, excelConfig);
//                }
//                System.out.println(value);
//                if (StringUtils.isEmpty(excelConfig.target()) || excelField.getField().getType().getName().contains("java.util.List")) {
//                    setCellValue(cell, value, excelConfig);
//                }
//                else{
//                    Field declaredField = value.getClass().getDeclaredField(excelConfig.target());
//                   Object o = getFieldValue(declaredField, value, excelConfig);
//                    setCellValue(cell, o, excelConfig);
//                }

//                Object value = getFieldValue(field, item, excelConfig);
//                if (StringUtils.isNotEmpty(excelConfig.target()) || excelField.getField().getType().getName().contains("java.util.List")) {
//                    Field declaredField = value.getClass().getDeclaredField(excelConfig.target());
//                    value = getFieldValue(declaredField, value, excelConfig);
//                }
//                Object val = conversion(value, excelConfig);
//                setCellValue(cell, val, excelConfig);
//                if (StringUtils.isEmpty(excelConfig.target()) || excelField.getField().getType().getName().contains("java.util.List")) {
//                    Object value = getFieldValue(field, item, excelConfig);
//                    setCellValue(cell, value, excelConfig);
//                }
//                else{
//
//                }
//            } catch (Exception e) {
//                log.error("填充Excel数据异常，字段: {}, 错误: {}", field.getName(), e.getMessage());
//                cell.setCellValue(excelConfig.defaultValue());
//            }
//        });
//    }

    /**
     * 设置单元格值
     */
    private static void setCellValue(Cell cell, Object value, Excel excelConfig) {
        String cellValue = (value == null) ? excelConfig.defaultValue() : value.toString();
        if (StringUtils.isNotEmpty(excelConfig.target())) {
            cellValue = getNestedFieldValue(value, excelConfig.target(), excelConfig);
        }

        cell.setCellValue(cellValue);
    }

    /**
     * 检查是否是List类型
     */
//    private static boolean isListType(Field field) {
//        return List.class.isAssignableFrom(field.getType());
//    }

//    private static boolean isListType(Object value) {
//        return value instanceof List;
//    }

    /**
     * 获取嵌套字段值
     */
    private static String getNestedFieldValue(Object obj, String targetFieldName, Excel excel) {
        try {
            if (!excel.isCollection()) {
                Field targetField = obj.getClass().getDeclaredField(targetFieldName);
                Object value = getFieldValue(targetField, obj, excel);
                return value != null ? value.toString() : "";
            } else {
                if(Objects.nonNull(obj)) {
                    List<?> list = (List<?>) obj;
                    if(list != null && list.size() > 0) {
                        ArrayList arr = new ArrayList();
                        for (Object item : list) {
                            Class<?> aClass = item.getClass();
                            Field declaredField1 = aClass.getDeclaredField(excel.target());
                            arr.add(getValue(declaredField1, item, excel).toString());
                        }
                        return String.join(",", arr);
                    }
                }
            }
            return "";
        } catch (Exception e) {
            log.warn("获取嵌套字段值失败: {}", e.getMessage());
            return "";
        }
    }

    /**
     * 获取字段值
     */
    private static Object getFieldValue(Field field, Object obj, Excel excelConfig) throws Exception {
        field.setAccessible(true);
        Object value = field.get(obj);
        if (value == null) {
            return null;
        }
        // 处理List类型字段
//        if (isListType(field)) {
//            return handleListValue((List<?>) value, excelConfig);
//        }
//         处理表达式转换
        value = applyValueConversion(value, excelConfig);
        // 处理日期格式化
        value = formatDateValue(value, excelConfig.dateFormat());
        return value;
    }

//    private static Object conversion(Object value, Excel excelConfig) {
//        // 处理表达式转换
//        value = applyValueConversion(value, excelConfig);
//        // 处理日期格式化
//        value = formatDateValue(value, excelConfig.dateFormat());
//        return value;
//    }

    /**
     * 格式化日期值
     */
    private static Object formatDateValue(Object value, String dateFormat) {
        if (value instanceof Date && StringUtils.isNotEmpty(dateFormat)) {
            return new SimpleDateFormat(dateFormat).format((Date) value);
        }
        return value;
    }

    /**
     * 处理List类型值
     */
//    private static String handleListValue(List<?> list, Excel excelConfig) {
//        if (StringUtils.isEmpty(excelConfig.target())) {
//            throw new ExcelExportException();
//        }
//
//        List<String> values = new ArrayList<>();
//        for (Object item : list) {
//            try {
//                Field targetField = item.getClass().getDeclaredField(excelConfig.target());
//                Object fieldValue = getFieldValue(targetField, item, excelConfig);
//                if (fieldValue != null) {
//                    values.add(fieldValue.toString());
//                }
//            } catch (Exception ex) {
//                log.warn("处理List类型值失败: {}", ex.getMessage());
//            }
//        }
//        return String.join(",", values);
//    }

    /**
     * 应用值转换
     */
    private static Object applyValueConversion(Object value, Excel excelConfig) {
        if (StringUtils.isNotEmpty(excelConfig.exp())) {
            return convertByExpression(value.toString(), excelConfig.exp());
        }
        return value;
    }

    /**
     * 根据表达式转换值
     */
    public static String convertByExpression(String propertyValue, String converterExp) {
        if (!StringUtils.isNotEmpty(propertyValue) || !StringUtils.isNotEmpty(converterExp)) {
            return propertyValue;
        }

        return Arrays.stream(converterExp.split(","))
                .map(item -> item.split("="))
                .filter(values -> values.length == 2 && values[0].equals(propertyValue))
                .findFirst()
                .map(values -> values[1])
                .orElse(propertyValue);
    }
    /**
     * 填充数据单元格
     */
//    private static <T> void fillDataCell(Row row,
//                                         int columnIndex,
//                                         T dataItem,
//                                         ExcelField excelField) {
//        Excel excelConfig = excelField.getExcel();
//        Field field = excelField.getField();
//        try {
//            Object cellValue = getCellValue(dataItem, field, excelConfig);
//            String displayValue = convertToString(cellValue, excelConfig.defaultValue());
//            Cell cell = row.createCell(columnIndex);
//            cell.setCellValue(displayValue);
//        } catch (Exception e) {
//            throw new ExcelExportException();
//        }
//    }

    /**
     * 获取单元格值
     */
//    private static <T> Object getCellValue(T dataItem, Field field, Excel excelConfig)
//            throws ReflectiveOperationException {
//        field.setAccessible(true);
//        Object value = field.get(dataItem);
//
//        if (StringUtils.isNotEmpty(excelConfig.target()) && !isCollectionType(field)) {
//            return getNestedFieldValue(value, excelConfig.target());
//        }
//        return value;
//    }

    /**
     * 获取Excel字段列表
     *
     * @param clazz 目标类类型
     * @param type  Excel操作类型
     * @param <T>   泛型类型
     * @return 排序后的Excel字段列表
     */
    private static <T> List<ExcelField> getExcelFields(Class<T> clazz, Excel.Type type) {
        return Arrays.stream(clazz.getDeclaredFields())
                .flatMap(field -> extractExcelFields(field, type))
                .sorted(Comparator.comparingInt(field -> field.getExcel().sort()))
                .collect(Collectors.toList());
    }

    /**
     * 检查是否为集合类型
     */
    private static boolean isCollectionType(Field field) {
        return field.getType().getName().contains("java.util.List");
    }

    /**
     * 将值转换为字符串表示
     */
//    private static String convertToString(Object value, String defaultValue) {
//        if (value == null) {
//            return defaultValue;
//        }
//        return value.toString();
//    }

    /**
     * 从单个字段中提取Excel字段配置
     *
     * @param field 待检查的字段
     * @param type  Excel操作类型
     * @return Excel字段流
     */
    private static Stream<ExcelField> extractExcelFields(Field field, Excel.Type type) {
        List<ExcelField> fields = new ArrayList<>();

        // 处理@Excel注解
        Optional.ofNullable(field.getAnnotation(Excel.class))
                .filter(excel -> isTypeMatch(excel.type(), type))
                .ifPresent(excel -> fields.add(new ExcelField(excel, field)));

        // 处理@Excels注解
        Optional.ofNullable(field.getAnnotation(Excels.class))
                .map(Excels::value)
                .ifPresent(excels -> Arrays.stream(excels)
                        .filter(excel -> isTypeMatch(excel.type(), type))
                        .forEach(excel -> fields.add(new ExcelField(excel, field))));
        return fields.stream();
    }

    /**
     * 检查Excel类型是否匹配
     *
     * @param annotationType 注解上的类型
     * @param targetType     目标类型
     * @return 是否匹配
     */
    private static boolean isTypeMatch(Excel.Type annotationType, Excel.Type targetType) {
        return annotationType == targetType || annotationType == Excel.Type.ALL;
    }

    /**
     * 创建Excel标题行
     *
     * @param sheet       Excel工作表对象
     * @param excelFields Excel字段配置列表
     */
    private static void createTitle(Sheet sheet, List<ExcelField> excelFields) {
        // 创建标题行并设置行高
        Row titleRow = sheet.createRow(0);
        titleRow.setHeightInPoints(26);

        // 遍历所有Excel字段配置
        IntStream.range(0, excelFields.size())
                .forEach(index -> {
                    ExcelField field = excelFields.get(index);
                    Excel excelConfig = field.getExcel();

                    // 创建单元格并设置值
                    Cell cell = titleRow.createCell(index);
                    cell.setCellValue(excelConfig.title());

                    // 设置列宽（考虑像素到字符宽度的转换）
                    sheet.setColumnWidth(index, calculateColumnWidth(excelConfig.width()));
                });
    }

    /**
     * 计算Excel列宽（字符宽度转像素值）
     *
     * @param charWidth 字符宽度
     * @return 像素宽度
     */
    private static int calculateColumnWidth(double charWidth) {
        // Excel中1个字符宽度≈256像素，0.72为调整系数
        return (int) ((charWidth + 0.72) * 256);
    }

    /**
     * 合并父字段单元格
     */
    private static void mergeParentCells(Sheet sheet, int rowIndex, int columnIndex) {
        // 合并当前单元格与上一行的对应单元格
        sheet.addMergedRegion(new CellRangeAddress(
                rowIndex - 1, rowIndex, columnIndex, columnIndex));
    }
}
